import {
    Controller, Get, Post, Param, Body, UsePipes, Res, Req, UploadedFile,
    ValidationPipe, UseInterceptors, Query, Put, Delete
} from '@nestjs/common';
import { Injectable, Inject, forwardRef, InternalServerErrorException, BadRequestException } from '@nestjs/common';
import { ApiTags, ApiOperation, ApiProperty, ApiQuery, ApiBody } from '@nestjs/swagger';
import { TransformInterceptor } from 'src/interceptor/transform.interceptor';
import { FileInterceptor } from '@nestjs/platform-express';
import { StatObjectDto, CopyObjectDto } from './object.dto';
import { Response } from 'express';
import { MinioClient } from '../app.minio';
const Fs = require('fs')
const path = require('path')

@ApiTags('对象')
@Controller('object')
@UseInterceptors(TransformInterceptor)
export class ObjectController {

    @Post('upload')
    @ApiOperation({ summary: '上传一个对象' })
    @UseInterceptors(FileInterceptor('file'))
    async putObject(@UploadedFile() file: Express.Multer.File, @Body() body: { objectName: '', bucketName: '' }) {
        console.log(body)
        return await new MinioClient().putObject(file, body).then(res => {
            console.log(res)
            return { key: res }
        });
    }

    @Get('getObject')
    @ApiOperation({ summary: '下载对象' })
    async getObject(@Query('bucketName') bucketName: string, @Query('objectName') objectName: string, @Res() res: Response) {
        const readerStream = await new MinioClient().getObject(bucketName, objectName);
        readerStream.on('data', chunk => {
            res.write(chunk, 'binary');
        });
        res.set({
            'Content-Type': 'application/octet-stream',
            'Content-Disposition': 'attachment; filename=' + objectName,
        });
        readerStream.on('end', () => {
            res.end();
        });
        readerStream.on('error', err => {
            console.log(err.stack);
            throw new BadRequestException(err.stack);
        });
    }

    @Post('copyObject')
    @ApiOperation({ summary: '将源对象拷贝到指定存储桶的新对象中' })
    async copyObject(@Body() body: CopyObjectDto) {
        return await new MinioClient().copyObject(body);
    }

    @Post('statObject')
    @ApiOperation({ summary: '获取对象的元数据' })
    async statObject(@Body() body: StatObjectDto) {
        return await new MinioClient().statObject(body);
    }

    @Post('removeObject')
    @ApiOperation({ summary: '删除一个对象' })
    async removeObject(@Body() body: StatObjectDto) {
        return await new MinioClient().removeObject(body);
    }
    @Post('upload/checkSnippet')
    @ApiOperation({ summary: '检查是否已有上传完的文件切片' })
    async checkSnippet(@Body() body) {
        const { hash } = body
        const chunksPath = path.join(__dirname, '../../public/cache', hash, '/')
        let chunksFiles = []
        if (Fs.existsSync(chunksPath)) {
            // 切片文件
            chunksFiles = Fs.readdirSync(chunksPath)
        }
        return chunksFiles
    }

    @Post('upload/snippet')
    @ApiOperation({ summary: '上传文件切片' })
    @UseInterceptors(FileInterceptor('file'))
    async snippet(@UploadedFile() file: Express.Multer.File, @Body() body) {

        const chunksPath = path.join(__dirname, '../../public/cache', body.hash, '/')
        const chunksFileName = path.join(__dirname, `../../public/cache`, body.hash, '/', body.hash + '_' + body.index)
        if (!Fs.existsSync(chunksPath)) {
            Fs.mkdirSync(chunksPath)
        }
        if (Fs.existsSync(chunksFileName)) {
            return 1
        } else {
            Fs.writeFileSync(chunksFileName, file.buffer);
            return 1
        }

    }
    @Post('upload/merge')
    @ApiOperation({ summary: '合并文件' })
    async merge(@Body() body) {
        const dirPath = path.join(__dirname, `../../public/cache`, body.hash, '/')
        const filePath = path.join(__dirname, `../../public`, body.name) // 合并文件
        await mergeFile(dirPath, filePath, body.hash, body.total)
        return await new MinioClient().putObject2(filePath, body).then(res => {
            Fs.unlinkSync(filePath)
            return { key: res }
        });

        function mergeFile(dirPath, filePath, hash, total): Promise<void> {
            return new Promise((resolve, reject) => {
                Fs.readdir(dirPath, (err, files) => {
                    if (err) {
                        return reject(err)
                    }
                    if (files.length !== total || !files.length) {
                        return reject('上传失败，切片数量不符')
                    }

                    const fileWriteStream = Fs.createWriteStream(filePath)
                    function merge(i): Promise<void> {
                        return new Promise((resolve2, reject2) => {
                            // 合并完成
                            if (i === files.length) {
                                Fs.rmdir(dirPath, (err) => {
                                    if (err) console.log(err, 'rmdir')
                                })
                                return resolve2()
                            }

                            let chunkpath = dirPath + hash + '_' + i
                            Fs.readFile(chunkpath, (err, data) => {
                                if (err) return reject2(err)
                                // 将切片追加到存储文件
                                Fs.appendFile(filePath, data, () => {
                                    // 删除切片文件
                                    Fs.unlink(chunkpath, () => {
                                        // 递归合并
                                        resolve2(merge(i + 1))
                                    })
                                })
                            })

                        })
                    }
                    merge(0).then(() => {
                        // 默认情况下不需要手动关闭，但是在某些文件的合并并不会自动关闭可写流，比如压缩文件，所以这里在合并完成之后，统一关闭下
                        resolve(fileWriteStream.close())
                        // resolve()
                    })
                })
            })
        }
    }
}

